package com.dhcc.bpm.modules.log.controller;

import com.dhcc.bpm.common.utils.DateUtils;
import com.dhcc.bpm.common.utils.RedisTemplateUtil;
import com.dhcc.bpm.modules.api.service.IApiService;
import com.dhcc.bpm.modules.cost.entity.CostRecord;
import com.dhcc.bpm.modules.cost.service.ICostRecordService;
import com.dhcc.bpm.modules.log.entity.GatewayLog;
import com.dhcc.bpm.modules.log.entity.GatewayLogDay;
import com.dhcc.bpm.modules.log.entity.GatewayLogMinute;
import com.dhcc.bpm.modules.log.service.IGatewayLogDayService;
import com.dhcc.bpm.modules.log.service.IGatewayLogMinuteService;
import com.dhcc.bpm.modules.log.service.IGatewayLogService;
import com.dhcc.bpm.modules.orga.service.IAppService;
import com.dhcc.bpm.modules.orga.service.IOrgaService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @ClassName ApiLogTask
 * @Description 定时任务，收集API每日监控信息
 * @Author lijiacheng
 * @Date 2020/2/25 15:22
 * @Version V1.0
 */

@Slf4j
@Configuration
@EnableScheduling
public class ApiLogTask {

    @Autowired
    IGatewayLogService opcGatewayLogService;
    @Autowired
    IGatewayLogMinuteService opcGatewayLogMinuteService;
    @Autowired
    IGatewayLogDayService opcGatewayLogDayService;

    @Autowired
    ICostRecordService opcCostRecordService;
    @Autowired
    IOrgaService opcOrgaService;
    @Autowired
    IAppService opcAppService;
    @Autowired
    IApiService opcApiService;
    @Autowired
    RedisTemplateUtil redisUtil;
    /**
     * 每天定时任务
     */
    @Value("${scheduler.taskDay}")
    private Boolean taskDay;

    /**
     * 分钟定时任务
     */
    @Value("${scheduler.taskMinute}")
    private Boolean taskMinute;


    /**
     * @param
     * @return
     * @Description 每日两点执行，计算上一天API交易信息
     * @Author LiJiaCheng
     * @Date 2020/2/26 13:22
     **/
    @Scheduled(cron = "${scheduler.taskYestodayScheduled}")
    public void task() {
        if (taskDay) {
            doTaskSql();
        }
    }

    /**
     * @param
     * @return
     * @Description 每日两分钟执行一次，记录今日交易信息
     * @Author LiJiaCheng
     * @Date 2020/2/26 13:22
     **/
    @Scheduled(cron = "${scheduler.taskTodayScheduled}")
    public void todayTask() {
        if (taskMinute) {
            doTodayTaskSql();
        }
    }

    /**
     * @param
     * @return void
     * @Description 每分钟执行，计算上五分钟前某分钟API交易信息
     * @Author LiJiaCheng
     * @Date 2020/2/26 13:22
     **/
    @Scheduled(cron = "${scheduler.taskMinuteScheduled}")
    public void minuteTask() {
        if (taskMinute) {
            doMinuteTaskSql();
        }
    }

    public void doTaskSql() {
        log.info("昨日统计定时任务已启动{}", DateUtils.getSystemDateOfString());
        GatewayLog opcGatewayLog = new GatewayLog();
        //获取昨天日期
        String targetDate = opcGatewayLogService.getDSNowDate();
        //--------start-------删除redis里存的apicode和系统---------------
        Set<Object> list = redisUtil.hkeys("apiCode:apiCode" + targetDate);
        for (Object sysName : list) {
            redisUtil.hdel("apiCode:apiCode" + targetDate, sysName.toString());
        }
        Set<Object> sysList = redisUtil.hkeys("apiCode:sys" + targetDate);
        for (Object sysName : sysList) {
            redisUtil.hdel("apiCode:sys" + targetDate, sysName.toString());
        }
        //---------------end---------------------------------------------
        opcGatewayLog.setRequestDate(targetDate);
        //获取网关收集的日志信息
        List<GatewayLog> opcGatewayLogList = opcGatewayLogService.findList(opcGatewayLog);
        List<GatewayLogDay> opcGatewayLogDayList = opcGatewayLogService.findListGroup(opcGatewayLog);
        for (GatewayLogDay gatewayLogDay : opcGatewayLogDayList) {
            if (StringUtils.isBlank(gatewayLogDay.getOpcApiCode()) || StringUtils.isBlank(gatewayLogDay.getOpcAppCode()) || StringUtils.isBlank(gatewayLogDay.getOpcOrgaCode()) || StringUtils.isBlank(gatewayLogDay.getEnv())) {
                continue;
            }
            //把筛选出来的信息放入OpcGatewayLogDay里
            GatewayLogDay opcGatewayLogDay = new GatewayLogDay();
            opcGatewayLogDay.setOpcApiCode(gatewayLogDay.getOpcApiCode());
            opcGatewayLogDay.setOpcAppCode(gatewayLogDay.getOpcAppCode());
            opcGatewayLogDay.setOpcOrgaCode(gatewayLogDay.getOpcOrgaCode());
            opcGatewayLogDay.setRequestDate(targetDate);
            //获取OpcGatewayLogDay中已存在的符合要求的数据，判断是新增OR更新
            GatewayLogDay logDay = opcGatewayLogService.findDailyData(opcGatewayLogDay);
            //求和，求平均值。请求量求和，响应时间求平均数，请求耗时求平均数
            opcGatewayLogDay.setRequestNum(gatewayLogDay.getRequestNum());
            opcGatewayLogDay.setRequestConsume(gatewayLogDay.getRequestConsume());
            opcGatewayLogDay.setMessageSize(gatewayLogDay.getMessageSize());
            opcGatewayLogDay.setEnv(gatewayLogDay.getEnv());
            //筛选出状态码不为200的请求，得出失败请求数
            List<GatewayLog> failRequest = opcGatewayLogList.stream()
                    .filter((GatewayLog log) -> gatewayLogDay.getOpcApiCode().equals(log.getOpcApiCode()))
                    .filter((GatewayLog log) -> gatewayLogDay.getOpcAppCode().equals(log.getOpcAppCode()))
                    .filter((GatewayLog log) -> gatewayLogDay.getOpcOrgaCode().equals(log.getOpcOrgaCode()))
                    .filter((GatewayLog log) -> gatewayLogDay.getEnv().equals(log.getEnvType()))
                    .filter((GatewayLog log) -> !"200".equals(log.getResponseStatus()))
                    .collect(Collectors.toList());
            opcGatewayLogDay.setFailRequestNum(failRequest.size());
            //筛选出状态码不为超时的请求，得出响应请求数
            List<GatewayLog> responseNum = opcGatewayLogList.stream()
                    .filter((GatewayLog log) -> gatewayLogDay.getOpcApiCode().equals(log.getOpcApiCode()))
                    .filter((GatewayLog log) -> gatewayLogDay.getOpcAppCode().equals(log.getOpcAppCode()))
                    .filter((GatewayLog log) -> gatewayLogDay.getOpcOrgaCode().equals(log.getOpcOrgaCode()))
                    .filter((GatewayLog log) -> gatewayLogDay.getEnv().equals(log.getEnvType()))
                    .filter((GatewayLog log) -> !"G0504".equals(log.getResponseStatus()))
                    .collect(Collectors.toList());
            opcGatewayLogDay.setResponseNum(responseNum.size());
            //获取当日缴费信息
            CostRecord opcCostRecord = new CostRecord();
            opcCostRecord.setCostDate(targetDate);
            //还要加上机构ID和APIID
            opcCostRecord.setOrgaId(gatewayLogDay.getOpcOrgaCode());
            opcCostRecord.setAppId(gatewayLogDay.getOpcAppCode());
            opcCostRecord.setApiId(gatewayLogDay.getOpcApiCode());
            //计算费用总和
            List<CostRecord> opcCostRecordList = opcCostRecordService.find(opcCostRecord);
            BigDecimal costAmount = new BigDecimal(0);
            for (CostRecord costRecord : opcCostRecordList) {
                costAmount = costAmount.add(costRecord.getCostAmount());
            }
            opcGatewayLogDay.setCostAmount(costAmount);
            //如果当天信息存在，则更新，否则新增
            if (logDay == null) {
                opcGatewayLogService.insertDailyData(opcGatewayLogDay);
            } else {
                opcGatewayLogDay.setId(logDay.getId());
                opcGatewayLogService.updateDailyData(opcGatewayLogDay);
            }
        }
    }


    public void doTodayTaskSql() {
        log.info("今日统计定时任务已启动{}", DateUtils.getSystemDateOfString());
        GatewayLogMinute opcGatewayLogMinute = new GatewayLogMinute();
        //获取今天日期
        String time = opcGatewayLogService.getNowTime();
        String targetDate = time.substring(0, 8);
        opcGatewayLogMinute.setRequestDate(targetDate);
        //从minute表中获取今日每分钟的统计信息
        List<GatewayLogMinute> opcGatewayLogMinuteList = opcGatewayLogService.findMinuteList(opcGatewayLogMinute);
        List<GatewayLogDay> opcGatewayLogDayList = opcGatewayLogService.findMinuteListGroup(opcGatewayLogMinute);
        for (GatewayLogDay gatewayLogDay : opcGatewayLogDayList) {
            if (StringUtils.isBlank(gatewayLogDay.getOpcApiCode()) || StringUtils.isBlank(gatewayLogDay.getOpcAppCode()) || StringUtils.isBlank(gatewayLogDay.getOpcOrgaCode()) || StringUtils.isBlank(gatewayLogDay.getEnv())) {
                continue;
            }
            //遍历API
            log.info("开始统计机构代码为{}且渠道代码为{}的API({})数据", gatewayLogDay.getOpcOrgaCode(), gatewayLogDay.getOpcAppCode(), gatewayLogDay.getOpcApiCode());
            //Filter筛选出符合机构,渠道和API的日志
            List<GatewayLogMinute> opcGatewayLogMinutes = opcGatewayLogMinuteList.stream()
                    .filter((GatewayLogMinute log) -> gatewayLogDay.getOpcApiCode().equals(log.getOpcApiCode()))
                    .filter((GatewayLogMinute log) -> gatewayLogDay.getOpcAppCode().equals(log.getOpcAppCode()))
                    .filter((GatewayLogMinute log) -> gatewayLogDay.getOpcOrgaCode().equals(log.getOpcOrgaCode()))
                    .collect(Collectors.toList());
            if (opcGatewayLogMinutes.size() != 0) {
                //把筛选出来的信息放入OpcGatewayLogDay里
                GatewayLogDay opcGatewayLogDay = new GatewayLogDay();
                opcGatewayLogDay.setOpcApiCode(gatewayLogDay.getOpcApiCode());
                opcGatewayLogDay.setOpcAppCode(gatewayLogDay.getOpcAppCode());
                opcGatewayLogDay.setOpcOrgaCode(gatewayLogDay.getOpcOrgaCode());
                opcGatewayLogDay.setRequestDate(targetDate);
                //获取OpcGatewayLogDay中已存在的符合要求的数据，判断是新增OR更新
                GatewayLogDay logDay = opcGatewayLogService.findDailyData(opcGatewayLogDay);
                //求和，求平均值。请求量求和，失败量总量，响应时间求平均数，请求耗时求平均数
                opcGatewayLogDay.setFailRequestNum((opcGatewayLogMinutes.stream().mapToInt(GatewayLogMinute::getFailRequestNum).sum()));
                opcGatewayLogDay.setResponseNum((opcGatewayLogMinutes.stream().map(gatewayLogMinute -> {
                    if (null == gatewayLogMinute.getResponseNum()) {
                        gatewayLogMinute.setResponseNum(gatewayLogMinute.getRequestNum());
                    }
                    return gatewayLogMinute;
                }).mapToInt(GatewayLogMinute::getResponseNum).sum()));
                //计算请求总笔数、平均响应时间，平均响应时间 = (sum（分钟请求数*分钟平均响应时间）)/总请求数
                int requestNum = 0;
                int requestConsume = 0;
                for (GatewayLogMinute numRecord : opcGatewayLogMinutes) {
                    requestNum = requestNum + numRecord.getRequestNum();
                    requestConsume = requestConsume + (numRecord.getRequestNum()) * (numRecord.getRequestConsume());
                }
                opcGatewayLogDay.setRequestNum(requestNum);
                opcGatewayLogDay.setRequestConsume(requestConsume / requestNum);

                //计算费用总和
                BigDecimal costAmount = new BigDecimal(0);
                for (GatewayLogMinute costRecord : opcGatewayLogMinutes) {
                    costAmount = costAmount.add(costRecord.getCostAmount());
                }
                opcGatewayLogDay.setCostAmount(costAmount);
                //计算报文大小
                int messageSize = 0;
                for (GatewayLogMinute data : opcGatewayLogMinutes) {
                    messageSize = messageSize + (data.getMessageSize()) * (data.getRequestNum());
                }
                opcGatewayLogDay.setMessageSize(messageSize / requestNum);
                opcGatewayLogDay.setEnv(opcGatewayLogMinutes.get(0).getEnv());
                //如果当天信息存在，则更新，否则新增
                if (logDay == null) {
                    opcGatewayLogService.insertDailyData(opcGatewayLogDay);
                } else {
                    opcGatewayLogDay.setId(logDay.getId());
                    opcGatewayLogService.updateDailyData(opcGatewayLogDay);
                }
            }
        }
    }


    public void doMinuteTaskSql() {
        log.info("分钟定时任务已启动{}", DateUtils.getSystemDateOfString());
        GatewayLog opcGatewayLog = new GatewayLog();
        //获取五分钟前时间
        String time = opcGatewayLogService.getNowTime();
        String targetDate = time.substring(0, 8);
        String targetTime = time.substring(8, 12);
        opcGatewayLog.setRequestDate(targetDate);
        opcGatewayLog.setRequestTime(targetTime + "%");
        //获取网关收集的日志信息
        List<GatewayLog> opcGatewayLogList = opcGatewayLogService.findList(opcGatewayLog);
        List<GatewayLogDay> opcGatewayLogDayList = opcGatewayLogService.findListGroup(opcGatewayLog);
        for (GatewayLogDay gatewayLogDay : opcGatewayLogDayList) {
            if (StringUtils.isBlank(gatewayLogDay.getOpcApiCode()) || StringUtils.isBlank(gatewayLogDay.getOpcAppCode()) || StringUtils.isBlank(gatewayLogDay.getOpcOrgaCode()) || StringUtils.isBlank(gatewayLogDay.getEnv())) {
                continue;
            }
            //把筛选出来的信息放入OpcGatewayLogDay里
            GatewayLogMinute opcGatewayLogMinute = new GatewayLogMinute();
            opcGatewayLogMinute.setOpcApiCode(gatewayLogDay.getOpcApiCode());
            opcGatewayLogMinute.setOpcAppCode(gatewayLogDay.getOpcAppCode());
            opcGatewayLogMinute.setOpcOrgaCode(gatewayLogDay.getOpcOrgaCode());
            opcGatewayLogMinute.setRequestDate(targetDate);
            opcGatewayLogMinute.setRequestTime(targetTime + "00");
            opcGatewayLogMinute.setBusinessTime(targetDate + targetTime + "00");
            //获取OpcGatewayLogDay中已存在的符合要求的数据，判断是新增OR更新
            GatewayLogMinute logMinute = opcGatewayLogService.findMinuteData(opcGatewayLogMinute);
            //设置雪花主键
            //求和，求平均值。请求量求和，响应时间求平均数，请求耗时求平均数
            opcGatewayLogMinute.setRequestNum(gatewayLogDay.getRequestNum());
            opcGatewayLogMinute.setRequestConsume(gatewayLogDay.getRequestConsume());
            opcGatewayLogMinute.setMessageSize(gatewayLogDay.getMessageSize());
            opcGatewayLogMinute.setEnv(gatewayLogDay.getEnv());
            //筛选出状态码不为200的请求，得出失败请求数
            List<GatewayLog> opcGatewayLogs = opcGatewayLogList.stream()
                    .filter((GatewayLog log) -> gatewayLogDay.getOpcApiCode().equals(log.getOpcApiCode()))
                    .filter((GatewayLog log) -> gatewayLogDay.getOpcAppCode().equals(log.getOpcAppCode()))
                    .filter((GatewayLog log) -> gatewayLogDay.getOpcOrgaCode().equals(log.getOpcOrgaCode()))
                    .filter((GatewayLog log) -> gatewayLogDay.getEnv().equals(log.getEnvType()))
                    .collect(Collectors.toList());
            List<GatewayLog> failRequest = opcGatewayLogs.stream()
                    .filter((GatewayLog log) -> !"200".equals(log.getResponseStatus()))
                    .collect(Collectors.toList());
            opcGatewayLogMinute.setFailRequestNum(failRequest.size());
            //筛选出状态码不为超时的请求，得出请求响应数
            List<GatewayLog> responseNum = opcGatewayLogs.stream()
                    .filter((GatewayLog log) -> !"G0504".equals(log.getResponseStatus()))
                    .collect(Collectors.toList());
            opcGatewayLogMinute.setResponseNum(responseNum.size());

            //获取当日缴费信息
            CostRecord opcCostRecord = new CostRecord();
            opcCostRecord.setCostDate(targetDate);
            opcCostRecord.setCostTime(targetTime + "%");
            //还要加上机构ID和APIID
            opcCostRecord.setOrgaId(gatewayLogDay.getOpcOrgaCode());
            opcCostRecord.setAppId(gatewayLogDay.getOpcAppCode());
            opcCostRecord.setApiId(gatewayLogDay.getOpcApiCode());
            //计算费用总和
            List<CostRecord> opcCostRecordList = opcCostRecordService.find(opcCostRecord);
            BigDecimal costAmount = new BigDecimal(0);
            for (CostRecord costRecord : opcCostRecordList) {
                costAmount = costAmount.add(costRecord.getCostAmount());
            }
            opcGatewayLogMinute.setCostAmount(costAmount);
            //------------update wjh---2020年11月19日15:44:26----------------
            String sysName = "";
            //判断redis里有没有，有的话更新下，没有的话给redis里也存入<apiCode,生产系统>，<生产系统,数量>。
            Object redisSysName = redisUtil.hget("apiCode:apiCode" + targetDate, gatewayLogDay.getOpcApiCode());
            if (redisSysName == null) {
                //如果是空。存进去----根据apiCode查对应系统
                String sysNames = opcGatewayLogService.findSysName(gatewayLogDay.getOpcApiCode());
                //存入时，设置24小时后自动删除
                boolean sysInsert = redisUtil.hset("apiCode:apiCode" + targetDate, gatewayLogDay.getOpcApiCode(), sysNames);
                if (sysInsert) {
                    sysName = sysNames;
                }
            } else {
                sysName = redisSysName.toString();
            }
            Object redisSysNum = redisUtil.hget("apiCode:sys" + targetDate, sysName + time.substring(0, 10));
            int num = opcGatewayLogs.size();
            if (redisSysNum != null) {
                //累加
                num += Integer.parseInt(redisSysNum.toString());
            }
            //更新redis 生产系统的访问数量,没有就插进入，有就更新下
            redisUtil.hset("apiCode:sys" + targetDate, sysName + time.substring(0, 10), num);
            //------------update wjh---2020年11月19日15:44:26----------------
            //如果当天信息存在，则更新，否则新增
            log.info("分钟定时任务入库数据：{}", opcGatewayLogMinute);
            if (logMinute == null) {
                opcGatewayLogService.insertMinuteData(opcGatewayLogMinute);
            } else {
                opcGatewayLogMinute.setId(logMinute.getId());
                opcGatewayLogService.updateMinuteData(opcGatewayLogMinute);
            }
        }
    }

    /**
     * @Description: 每天执行定时任务将3个月前的数据存入历史表里面
     * @Author： xfy
     * @date: 2022-03-24 16:52:00
     * @version: V1.0.0
     */
    @Transactional(rollbackFor = Exception.class)
    public void historyStatistics(int month) {
        //获取3个月前的日期
        String lastDdate = DateUtils.getLastMonth(month, "yyyyMMdd");
        //查询3个月前的数据
        List<GatewayLog> gatewayLogList = opcGatewayLogService.queryThirdMonthAgoLogList(lastDdate);
        List<GatewayLogMinute> gatewayLogMinuteList = opcGatewayLogService.queryThirdMonthAgoLogMinuteList(lastDdate);
        List<GatewayLogDay> gatewayLogDayList = opcGatewayLogService.queryThirdMonthAgoLogDayList(lastDdate);

        //将数据插入历史表
        opcGatewayLogService.saveBatch(gatewayLogList);
        opcGatewayLogMinuteService.saveBatch(gatewayLogMinuteList);
        opcGatewayLogDayService.saveBatch(gatewayLogDayList);

        //从记录表删除3个月前的数据
        gatewayLogList.forEach(gatewayLog -> opcGatewayLogService.removeById(gatewayLog.getId()));
        gatewayLogMinuteList.forEach(gatewayLogMinute -> opcGatewayLogMinuteService.removeById(gatewayLogMinute.getId()));
        gatewayLogDayList.forEach(gatewayLogDay -> opcGatewayLogDayService.removeById(gatewayLogDay.getId()));

        //统计历史表数据存入redis

    }

}






